#encoding=utf-8
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
from flask import Flask, jsonify, abort
from functools import wraps # cross domain wrap
from flask import make_response
from flask import g # need to be imported to be used
from flask.ext.script import Manager
app = Flask(__name__)
manager = Manager(app)

import os  # database
from flask import request # post
import MySQLdb
import MySQLdb.cursors
#import types
import hashlib
import base64 #encode
from flask.ext.httpauth import HTTPBasicAuth # to verify

auth = HTTPBasicAuth()

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer # to generate a token
from itsdangerous import BadSignature,SignatureExpired

def allow_cross_domain(fun):
    @wraps(fun)
    def wrapper_fun(*args, **kwargs):
        rst = make_response(fun(*args, **kwargs))
        rst.headers['Access-Control-Allow-Credentials'] = 'true'
        rst.headers['Access-Control-Allow-Origin'] = '*'
        allow_headers ='referer,accept,origin,user-agent,content-type'
        rst.headers['Access-Control-Allow-Headers'] = allow_headers
        rst.headers['Access-Control-Max-Age'] = 36000
        rst.headers['Access-Control-Allow-Methods'] = 'PUT,GET,POST,OPTIONS,DELETE'
        return rst
    return wrapper_fun
    
from datetime import timedelta
from flask import make_response, request, current_app
from functools import update_wrapper


def crossdomain(origin=None, methods=None, headers=None,
                max_age=21600, attach_to_all=True,
                automatic_options=True):
    if methods is not None:
        methods = ', '.join(sorted(x.upper() for x in methods))
    if headers is not None and not isinstance(headers, basestring):
        headers = ', '.join(x.upper() for x in headers)
    if not isinstance(origin, basestring):
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):
        max_age = max_age.total_seconds()
    def get_methods():
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):
        def wrapped_function(*args, **kwargs):
            if automatic_options and request.method == 'OPTIONS':
                resp = current_app.make_default_options_response()
            else:
                resp = make_response(f(*args, **kwargs))
            if not attach_to_all and request.method != 'OPTIONS':
                return resp

            h = resp.headers

            h['Access-Control-Allow-Origin'] = origin
            h['Access-Control-Allow-Methods'] = get_methods()
            h['Access-Control-Max-Age'] = str(max_age)
            h['Access-Control-Allow-Headers'] = 'referer,accept,origin,user-agent,content-type,Access-Token'
            return resp

        f.provide_automatic_options = False
        return update_wrapper(wrapped_function, f)
    return decorator
    
# database configuration
HOST = 'localhost'
USER = 'root'
PASSWORD = '000242wyf'
DBNAME = 'parlons'
# database configurations
conn = MySQLdb.connect ( host = HOST, user = USER,\
                        passwd =PASSWORD,\
                         charset='utf8')
cur = conn.cursor(MySQLdb.cursors.DictCursor)
#cur.execute('DROP DATABASE IF EXISTS parlons')
cur.execute('CREATE DATABASE IF NOT EXISTS parlons')
conn.select_db('parlons')

# Table Users
#cur.execute('DROP DATABASE IF EXISTS parlons')
cur.execute('create table if not exists users(\
`id` int not null auto_increment primary key,\
`username` varchar(20) not null unique key,\
`password` varchar(50) not null )')

# Table Message
cur.execute('CREATE TABLE IF NOT EXISTS messages(\
            `id` int not null auto_increment primary key,\
            `sender_id` int not null,\
            `receiver_id` int not null,\
            `content` text not null,\
            `created_date` text )')


#Table Tasks
# cur.execute('DROP TABLE IF EXISTS tasks')
# cur.execute('create table if not exists tasks(\
# `id` int not null auto_increment primary key,\
# `name` varchar(20) not null,\
# `description` text,\
# `user_id` int not null,\
# `begin_date` text not null,\
# `end_date` text not null,\
# `days` int not null,\
# `type` int not null,\
# `group_id` int )')
# about type:
    # 0 : default
    
#Table History
# cur.execute('create table if not exists history(\
# `task_id` int not null primary key,\
# `day` int not null,\
# `status` int not null )')
#about status:
    # 0 : null (not finished)
    # 1 : well done
    # 2 : prety good 
    # 3 : not well
    # 4 : bad



conn.commit()
cur.close()
conn.close()

def get_table_name(table_name):
    try:
        conn = MySQLdb.connect ( host = HOST, user = USER,\
                        passwd =PASSWORD, db = DBNAME, charset='utf8')
        cur = conn.cursor()
     	cur.execute("SELECT column_name FROM information_schema.columns WHERE table_schema='parlons' AND table_name=%s",[table_name])
        results = cur.fetchall()
        return results
    except Exception as e:
        print "Error in get_table: %s" % e

def to_json_one(info_name, col_names, results):
    try:
        data = {}
        for i in range(len(col_names)):
            data[str(col_names[i][0])] = results[0][i]
        return jsonify({info_name : data})
    except Exception as e:
        print "Error in to_json: %s" % e

def to_json(info_name, col_names, results):
    try:
    	datas = []
        for result in results:
            data = {}
            for i in range(len(col_names)):
                data[str(col_names[i][0])] = result[i]
            datas.append(data)
        return  jsonify({info_name : datas})    
    except Exception as e:
        print "Error in to_json: %s" % e

def get_json_content(request_json):
    content = []
    for key in request_json.keys():
        content.append(request_json[key])
    return content
    
#def insert_into(table_name,request_json):
#    try:
#        conn = MySQLdb.connect  ( host = HOST, user = USER,\
#                        passwd =PASSWORD, db = DBNAME, charset='utf8')
#        cur = conn.cursor()
#        content = [table_name]
#        print type(table_name)
#        sql = 'insert into %s('
#        for key in request_json.keys():
#            content.append(key)
#            sql += '%s,'
#        sql = sql[0:-1]
#        sql += ') values('
#        for key in request_json.keys():
#            print type(request_json[key])
#            content.append(request_json[key])
#            sql += '%s,'
##            if(type(request_json[key]) is types.IntType):
##                sql += '%d,'
##            if(type(request_json[key]) is types.FloatType):
##                sql += '%f,'
##            if(type(request_json[key]) is types.StringType):
##                sql += '%s,'
#        sql = sql[0:-1]
#        sql +=')'
#        print content
#        print sql
#        count = cur.execute(sql,content)
#        print 2
#        return 'Successful'
#    except MySQLdb.Error,e:
#        print "Mysql Error %d: %s " % (e.args[0], e.args[1])

# generate a token
def generate_auth_token(user_id, expiration = 6000):
    s = Serializer('SECRET_KEY', expires_in = expiration) # not the same 
    value = {'id':user_id}
    token = s.dumps(value)
    return token
    

def verify_auth_token(token):
    s = Serializer('SECRET_KEY')
    try:
        data = s.loads(token)
    except SignatureExpired:
        return None # valid token, but expired
    except BadSignature: # we shold import the class BadSignature to use it
        return None # invalid token
        #if the token is ok, return the user id
    return data['id']  #token is ok


@auth.verify_password
def verify_password(username, password):
    username = request.json['username']
    password = request.json['password']
    try:
        conn = MySQLdb.connect  ( host = HOST, user = USER,\
                    passwd =PASSWORD, db = DBNAME, charset='utf8')
        cur = conn.cursor()
        count = cur.execute('select password,id from users where username = %s', [username])
        if(count == 0):
            return False
        result = cur.fetchone() #results[1] is still a tuple
        pw_hash = result[0]
        user_id = result[1]
    except MySQLdb.Error,e:
        print "Mysql Error %d: %s " % (e.args[0], e.args[1])
    if (pw_hash == hashlib.sha1(password).hexdigest()):
        g.user_id = user_id
        g.username = username
        return True
    else:
        return False


## useless...
#def verify(username_or_token, password):
#    username_or_token = request.json['username']
#    password = request.json['password']
#    # first try to authenticate by token
#    user_id = verify_auth_token(username_or_token) # if not authenticated, return None
#    if not user_id: # token not authenticated
#        conn = MySQLdb.connect  ( host = HOST, user = USER,\
#                    passwd =PASSWORD, db = DBNAME, charset='utf8')
#        cur = conn.cursor()
#        count = cur.execute('select id,username from users where username = %s', [username_or_token])
#        if(count == 0):
#            return False
#        result = cur.fetchone() 
#        user_id = result[0]
#        username = result[1]
#        if not verify_password(username_or_token,password): # password not authenticated
#            return False
#    else:
#        conn = MySQLdb.connect  ( host = HOST, user = USER,\
#                    passwd =PASSWORD, db = DBNAME, charset='utf8')
#        cur = conn.cursor()
#        count = cur.execute('select username from users where id = %s', [user_id])
#        if(count == 0):
#            return False
#        result = cur.fetchone() 
#        username = result[0]
#    g.user_id = user_id
#    g.username = username
#    return True

# verify a token
# used to protect the pages not the datas
@app.route('/parlons/api/v1.0/users/verify', methods=['POST','OPTIONS'])
@crossdomain(origin="*",methods="POST,OPTIONS")
def verify_token():
    try:
       	token = request.json['token']
        result = verify_auth_token(token)
        if not result:
            return jsonify({ 'error': 'Bad Token' }), 404
#        else:
#            conn = MySQLdb.connect  ( host = HOST, user = USER,\
#                    passwd =PASSWORD, db = DBNAME, charset='utf8')
#            cur = conn.cursor()
#            count = cur.execute('select username from users where id = %s', [result])
#            if(count == 0):
#                return jsonify({ 'error': 'No Such User'}), 404
        return jsonify({ 'error': 'None' }),201
    except Exception as e:
        print "Error in verify_token: %s" % e
        
        
# get a token
@app.route('/parlons/api/v1.0/users/login', methods=['POST','OPTIONS'])
@auth.login_required  # if verify_password() returns true
@crossdomain(origin="*",methods="POST,OPTIONS")
def get_auth_token():
    token = generate_auth_token(g.user_id)
    return jsonify({ 'token': token.decode('ascii'), 'userId': g.user_id, 'username':g.username })


@app.route('/parlons/api/v1.0/users', methods=['GET'])
@crossdomain(origin="*")
def get_users():
    try:
        conn = MySQLdb.connect  ( host = HOST, user = USER,\
                        passwd =PASSWORD, db = DBNAME, charset='utf8')
        cur = conn.cursor()
        count = cur.execute('select id,username,password from users') # count is the number of results
        if(count == 0):
            abort(404,'There is no users at present.')
        results = cur.fetchall()
        return to_json('users',get_table_name('users'),results), 201
    except MySQLdb.Error,e:
        print "Mysql Error %d: %s " % (e.args[0], e.args[1])

# get the information of a certain user
@app.route('/parlons/api/v1.0/users/<int:userId>', methods=['GET',"OPTIONS"])
@crossdomain(origin="*")
def get_user(userId):
    try:
        conn = MySQLdb.connect  ( host = HOST, user = USER,\
                        passwd =PASSWORD, db = DBNAME, charset='utf8')
        cur = conn.cursor()
        count = cur.execute('select id,username,password from users where id = %s', [userId])
        if(count == 0):
            abort(404)
        results = cur.fetchall() #result[1] is still a tuple
        return to_json_one('user',get_table_name('users'),results), 201
    except MySQLdb.Error,e:
        print "Mysql Error %d: %s " % (e.args[0], e.args[1])
   

# register a new user
@app.route('/parlons/api/v1.0/users/sign_up', methods=['POST','OPTIONS'])
@crossdomain(origin="*",methods="POST,OPTIONS")
def create_user():
    try:
        conn = MySQLdb.connect  ( host = HOST, user = USER,\
                        passwd =PASSWORD, db = DBNAME, charset='utf8')
        cur = conn.cursor()
        check = cur.execute("select * from users where username = %s", [request.json['username']])
        if (check >0 ): # username exists
            jsonify({ 'error': 'User Exists'}), 400
        request.json['password'] = hashlib.sha1(request.json['password']).hexdigest()
        # the order may change after the modification
        cur.execute("insert into users(username,password) values (%s,%s)",get_json_content(request.json))
        cur.execute("SELECT LAST_INSERT_ID() from users") # should be auto increment
        result = cur.fetchone()
        user_id  = result[0]
        conn.commit()
        cur.close()
        conn.close()
        token = generate_auth_token(user_id)
        return jsonify({ 'token': token.decode('ascii'), 'userId': user_id, 'username':request.json['username'] })
    except MySQLdb.Error,e:
        print "Mysql Error %d: %s " % (e.args[0], e.args[1])


# @app.route('/parlons/api/v1.0/messages/<int:senderId>/<int:receiverId>', methods=['POST', 'OPTIONS'])
# @crossdomain(origin="*",methods="POST,OPTIONS")
# def send_message(senderId, receiverId):
#     try:
#         conn = MySQLdb.connect  ( host = HOST, user = USER,\
#                         passwd =PASSWORD, db = DBNAME, charset='utf8')
#         cur = conn.cursor()
#         cur.execute("insert into message(sender_id, receiver_id, content, created_date) values (%s, %s, %s, %s)",get_json_content(request.json))
#         cur.execute("SELECT LAST_INSERT_ID() from message") # should be auto increment


# get tasks of a user
# @app.route('/parlons/api/v1.0/users/<int:userId>/tasks', methods=['GET','OPTIONS'])
# @crossdomain(origin="*",methods="GET,OPTIONS")
# def get_tasks(userId):
#     try:
#         conn = MySQLdb.connect  ( host = HOST, user = USER,\
#                         passwd =PASSWORD, db = DBNAME, charset='utf8')
#         cur = conn.cursor()
#         check = cur.execute("select * from tasks where user_id = %s", [userId])
#         if (check == 0 ): # No tasks
#             return jsonify({ 'error': 'No Tasks' }), 404
#         results = cur.fetchall()
#         cur.close()
#         conn.close()
#         return to_json('tasks',get_table_name('tasks'),results), 201
#     except MySQLdb.Error,e:
#         print "Mysql Error %d: %s " % (e.args[0], e.args[1])

# create a new task
# @app.route('/parlons/api/v1.0/users/<int:userId>/tasks', methods=['POST','OPTIONS'])
# @crossdomain(origin="*",methods="POST,OPTIONS")
# def create_task(userId):
#     try:
#         conn = MySQLdb.connect  ( host = HOST, user = USER,\
#                         passwd =PASSWORD, db = DBNAME, charset='utf8')
#         cur = conn.cursor()
#         # the order may change after the modification
#         cur.execute("insert into tasks(name,description,user_id,begin_date,end_date,days,type) values (%s,%s,%s,%s,%s,%s,%s)",\
#         (request.json['name'],request.json['description'],request.json['user_id'],request.json['begin_date'],request.json['end_date'],request.json['days'],request.json['type']))
#         conn.commit()
#         cur.close()
#         conn.close()
#         return jsonify({ 'error': 'None' }), 201
#     except MySQLdb.Error,e:
#         print "Mysql Error %d: %s " % (e.args[0], e.args[1])



if __name__ == '__main__':
    manager.run()
